#include "VirtualMachine.hpp"
#include "Tokenizer.hpp"
#include "Parser.hpp"
#include "SyntaxTree.hpp"
#include <cstdio>
#include <cstdlib>

using namespace Welp;
using namespace std;

Variant Print(Variant& functionAndSelf, VirtualMachine* vm, const vector<Variant>& arguments)
{
  auto value = arguments[0].mNumber;
  printf("PRINT: %f\n", value);
  return Variant();
}

int main(void)
{
  //GraphVisualizer graph;
  //graph.BeginDiGraph();
  //graph.AddNode("A", "StateA", "circle");
  //graph.AddNode("B", "StateB");
  //graph.AddEdge("A", "B");
  //graph.EndGraph();
  //graph.Visualize();


  //local b = function(z)
  //{
  //  print((5 + 3 * 7) / z);
  //};

  //local f = 2;
  //b(f);
  //return "hello world";



  VirtualMachine vm;

  vm.mGlobalsTable.InsertOverwrite("print", Print, nullptr);


  auto chunk = new FunctionNode();
  chunk->mIsClosure = false;

  auto localVarB = chunk->AddChild(chunk->mStatements, new VariableDeclarationNode());
  localVarB->mName = "b";
  localVarB->mIsLocal = true;

  auto function = localVarB->AddChild(localVarB->mOptionalInitialValue, new FunctionNode());
  function->mParameters.push_back("z");
  function->mIsClosure = false;

  auto printCall = function->AddChild(function->mStatements, new FunctionCallNode());

  auto printVarRef = printCall->AddChild(printCall->mCallable, new VariableReferenceNode());
  printVarRef->mName = "print";

  auto divideNode = printCall->AddChild(printCall->mArguments, new BinaryOperatorNode());
  divideNode->mOperator = TokenType::Divide;

  auto addNode = divideNode->AddChild(divideNode->mLeft, new BinaryOperatorNode());
  addNode->mOperator = TokenType::Plus;

  auto zVarRef = divideNode->AddChild(divideNode->mRight, new VariableReferenceNode());
  zVarRef->mName = "z";

  auto five = addNode->AddChild(addNode->mLeft, new LiteralNode());
  five->mValue = Variant(5.0);

  auto multiplyNode = addNode->AddChild(addNode->mRight, new BinaryOperatorNode());
  multiplyNode->mOperator = TokenType::Multiply;
  
  auto three = multiplyNode->AddChild(multiplyNode->mLeft, new LiteralNode());
  three->mValue = Variant(3.0);
  
  auto seven = multiplyNode->AddChild(multiplyNode->mRight, new LiteralNode());
  seven->mValue = Variant(7.0);


  auto localVarF = chunk->AddChild(chunk->mStatements, new VariableDeclarationNode());
  localVarF->mName = "f";
  localVarF->mIsLocal = true;

  auto two = localVarF->AddChild(localVarF->mOptionalInitialValue, new LiteralNode());
  two->mValue = Variant(2.0);


  auto bCall = chunk->AddChild(chunk->mStatements, new FunctionCallNode());
    
  auto bVarRef = bCall->AddChild(bCall->mCallable, new VariableReferenceNode());
  bVarRef->mName = "b";

  auto fVarRef = bCall->AddChild(bCall->mArguments, new VariableReferenceNode());
  fVarRef->mName = "f";

  auto returnNode = chunk->AddChild(chunk->mStatements, new ReturnNode());

  auto helloWorldStr = returnNode->AddChild(returnNode->mOptionalReturnValue, new LiteralNode());
  helloWorldStr->mValue = "hello world";

  Variant chunkFunction(chunk);


  Variant finalResult = chunkFunction.Call(&vm, vector<Variant>(), nullptr);


  Tokenizer tokenizer;

  tokenizer.mText = "++ Animal true if { 0.3\n 5 <\"hello\"9.99blah \"foo";

  Token token;

  do
  {
    tokenizer.ReadAndClassifyToken(&token);

    int a = 3;a = a;
  }
  while (token.mType != TokenType::Invalid);


  Token dfaToken;
  DfaTokenizer dfaTokenizer;
  dfaTokenizer.mText = "hello \n\n   \"what is the meaning of \\j this \\\" stuff\" test";

  do
  {
    dfaTokenizer.ReadToken(&dfaToken);

    int a = 3;a = a;
  }
  while (dfaToken.mType != TokenType::Eof && dfaToken.mType != TokenType::Invalid);

  //Parser parser;
  //parser.mTokenizer.mText = "{ if (5) { function; } }";

  //auto parseResult = parser.Parse();


  system("pause");
  return 0;
}

